/**
 * Generate code from /{{projectName}}-core/src/main/java/{{packageName}}/util/aspect/TenantAspect.java.hbs
 */
package cn.ibizlab.util.aspect;

import cn.ibizlab.util.domain.IEntity;
import cn.ibizlab.util.filter.ISearchContext;
import cn.ibizlab.util.security.AuthenticationUser;
import cn.ibizlab.util.tenant.TenantProperties;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 多租户切面
 *
 * @author generator
 */
@Aspect
@Order(10)
@Component
@Slf4j
@ConditionalOnExpression("'${ibiz.tenant.enabled:false}'.equals('true')")
public class TenantAspect
{
    @Autowired
    private TenantProperties tenantProperties;

    @Around("execution(* cn.ibizlab.core.*.service.*.*(..))")
    public Object fillTenantId(ProceedingJoinPoint point) throws Throwable {

        String tenantId = AuthenticationUser.getAuthenticationUser().getDc();
        if(ObjectUtils.isEmpty(tenantId)){
            tenantId = getTenantId();
        }
        String tenantIdColumn = tenantProperties.getTenantIdColumn();
        if(!ObjectUtils.isEmpty(tenantIdColumn) && !ObjectUtils.isEmpty(tenantId)){
            //匿名访问
            boolean isAnonymous =  SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null
                                                                              && SecurityContextHolder.getContext().getAuthentication() instanceof AnonymousAuthenticationToken;
            Object result;
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            try {
                //临时构造dc身份
                if(isAnonymous){
                    AuthenticationUser authenticationUser = AuthenticationUser.getAuthenticationUser();
                    authenticationUser.setDc(tenantId);
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(authenticationUser, null, authenticationUser.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
                //设置dc信息
                Object[] args = point.getArgs();
                if (args.length > 0) {
                    Object obj = args[0];
                    if(obj instanceof IEntity) {
                        IEntity entity = (IEntity) obj;
                        if(ObjectUtils.isEmpty(entity.get(tenantProperties.getTenantIdColumn())))
                            entity.set(tenantProperties.getTenantIdColumn(),tenantId);
                    }
                    else if (obj instanceof List) {
                        for(Object object : (List)obj) {
                            if(object != null && object instanceof IEntity) {
                                IEntity entity = (IEntity) object;
                                if(ObjectUtils.isEmpty(entity.get(tenantProperties.getTenantIdColumn())))
                                    entity.set(tenantProperties.getTenantIdColumn(),tenantId);
                            }
                        }
                    }
                    else if (obj instanceof ISearchContext){
                        ISearchContext context = (ISearchContext) obj;
                        context.set(tenantProperties.getTenantIdColumn(),tenantId);
                    }
                }
                //携带dc身份调用逻辑
                result = point.proceed();

            } finally {
                //设置为原始用户身份
                if(isAnonymous)
                    SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            return result;
        }
        return point.proceed();
    }


    /**
     * 从请求头中获取tenantId
     * @return
     */
    protected String getTenantId(){
        String tenantId = null;
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = requestAttributes.getRequest();
            if(request != null){
                tenantId = request.getHeader(tenantProperties.getTenantHeader());
                log.debug("tenant aspect fill tenantId: {}" , tenantId);
            }
        }
        return tenantId;
    }

}